package cloudContact.DAO;

import java.io.IOException;
import java.io.OutputStream;
import java.net.MalformedURLException;

import java.net.URL;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;

import org.mybeans.factory.RollbackException;

import cloudContact.MyDAOException;

import com.google.gdata.client.Query;
import com.google.gdata.client.Service.GDataRequest;
import com.google.gdata.client.contacts.ContactsService;
import com.google.gdata.data.Link;
import com.google.gdata.data.PlainTextConstruct;
import com.google.gdata.data.contacts.ContactEntry;
import com.google.gdata.data.contacts.ContactFeed;
import com.google.gdata.data.contacts.ContactGroupEntry;
import com.google.gdata.data.contacts.ContactGroupFeed;
import com.google.gdata.data.contacts.GroupMembershipInfo;
import com.google.gdata.util.ContentType;
import com.google.gdata.util.ServiceException;
import com.google.gdata.client.authn.oauth.*;

public class GoogleContactDAO {

	private static URL theUrl = null;

	private ContactsService theService;
	private Hashtable<String, GoogleContact> knownContacts;
	private Hashtable<ContactGroupEntry, ArrayList<GoogleContact>> knownGroups;
	private String session;
	
	// key is session id
	private static Hashtable<String, GoogleContactDAO> instances = new Hashtable<String, GoogleContactDAO>();
	
	public static GoogleContactDAO getInstanceForSession(String sessionId, GoogleOAuthParameters parameters)
	throws MalformedURLException, OAuthException{
		GoogleContactDAO instance;
		if (!instances.containsKey(sessionId)){
			instance = new GoogleContactDAO(parameters);
			instance.session = sessionId;
			instances.put(sessionId, instance);
		}else{
			instance = instances.get(sessionId);
		}
		return instance;
	}
	
	private GoogleContactDAO(GoogleOAuthParameters parameters)
			throws MalformedURLException, OAuthException {
		theService = new ContactsService("CloudContact-CloudContact-1");
		if (theUrl == null) {
			theUrl = new URL(
					"https://www.google.com/m8/feeds/contacts/default/full");
		}
		if (parameters == null)
			System.out.println("No OAuth Credentials to provide.");
		else
			theService.setOAuthCredentials(parameters, new OAuthHmacSha1Signer());

	}

	public void create(GoogleContact newContact) throws MyDAOException,
			IOException, ServiceException, RollbackException {
		setGeoLocation(newContact);
		ContactEntry entry = theService
				.insert(theUrl, newContact.getTheEntry());
		newContact.setTheEntry(entry);
		
		// uploading photo
		newContact.setImage(newContact.getImage());
		
		// update local cache
		knownContacts.put(newContact.getContactId(), newContact);
	}

	public void update(GoogleContact theContact) throws IOException,
			ServiceException, RollbackException, MyDAOException {
		URL editUrl = new URL(theContact.getTheEntry().getEditLink().getHref());
		setGeoLocation(theContact);
		theService.update(editUrl, theContact.getTheEntry());
	}

	public GoogleContact lookup(String id) throws IOException,
			ServiceException, RollbackException, MyDAOException {
		
		getAllContacts();
		
		GoogleContact contact = knownContacts.get(id);
		setGeoLocation(contact);
		
		return contact;
	}

	public void delete(GoogleContact theContact) throws IOException,
			ServiceException {
		knownContacts.remove(theContact.getContactId());
		theContact.getTheEntry().delete();
	}

	public Hashtable<String, GoogleContact> getAllContacts() throws IOException,
			ServiceException, RollbackException, MyDAOException {
		if (knownContacts != null)
			return knownContacts;
		Query myQuery = new Query(theUrl);
		myQuery.setMaxResults(10000);
		ContactFeed resultFeed = theService.query(myQuery, ContactFeed.class);
		List<ContactEntry> entries = resultFeed.getEntries();
		
		knownContacts = new Hashtable<String, GoogleContact>();
		
		for (int i = 0; i < entries.size(); i++) {
			ContactEntry entry = entries.get(i);
			GoogleContact contact = new GoogleContact(entry);
			knownContacts.put(contact.getContactId(), contact);
			setGeoLocation(contact);
		}

		System.out.println("getAllContacts(" + session + "): User has " + knownContacts.size() + " contacts");

		return knownContacts;
	}

	private void setGeoLocation(GoogleContact contact)
			throws RollbackException, MyDAOException {
		
		 UserDAO userDAO = UserDAO.getInstance();
		 Hashtable<String, String> allEmails = contact.getAllEmails();
		 for (String mail : allEmails.values()) {
			 User theUser = userDAO.lookupByGmail(mail);
			 if (theUser == null)
				 continue;
			 contact.setLastSeenCoor(theUser.getLastSeenLatitude(),
					 theUser.getLastSeenLongitude());
			 break;
		 }
	}

	public Hashtable<ContactGroupEntry, ArrayList<GoogleContact>> getAllContactsWithGroup()
			throws IOException, ServiceException, RollbackException,
			MyDAOException {
		if (knownGroups == null) {
			knownGroups = new Hashtable<ContactGroupEntry, ArrayList<GoogleContact>>();
		

		URL feedUrl = new URL(
				"https://www.google.com/m8/feeds/groups/default/full");

		Query myQuery = new Query(feedUrl);
		myQuery.setMaxResults(10000);

		ContactGroupFeed resultFeed = theService.query(myQuery,
				ContactGroupFeed.class);

		getAllContacts();

		System.out.println("User has " + resultFeed.getEntries().size()
				+ " groups:");

		for (ContactGroupEntry groupEntry : resultFeed.getEntries()) {

			ArrayList<GoogleContact> groupMembers = null;

			if (!knownGroups.contains(groupEntry.getTitle().getPlainText())) {
				groupMembers = new ArrayList<GoogleContact>();
				knownGroups.put(groupEntry, groupMembers);
			} else {
				groupMembers = knownGroups.get(groupEntry.getTitle());
			}

			for (GoogleContact gContact : knownContacts.values()) {
				if (gContact.getGroups().contains(groupEntry.getId())) {
					groupMembers.add(gContact);
				}
			}

			System.out.println("Group " + groupEntry.getTitle().getPlainText()
					+ " with ID:" + groupEntry.getId() + " has "
					+ groupMembers.size() + " contacts.");
		}
		}
		return knownGroups;
	}

	public ContactGroupEntry CreateGroup(String groupName) throws MalformedURLException,
			IOException, ServiceException {
		ContactGroupEntry group = new ContactGroupEntry();
		group.setTitle(new PlainTextConstruct(groupName));

		theService.insert(theUrl, group);
		
		return group;
	}

	public void AddContact2Group(GoogleContact contact, String group)
			throws IOException, ServiceException, RollbackException,
			MyDAOException {
		getAllContactsWithGroup();

		if (!knownGroups.contains(group)) {
			ContactGroupEntry groupEntry = CreateGroup(group);
			knownGroups.put(groupEntry, new ArrayList<GoogleContact>());
		}

		GroupMembershipInfo membership = new GroupMembershipInfo();
		membership.setHref(group);
		contact.getTheEntry().getGroupMembershipInfos().add(membership);
		theService.update(
				new URL(contact.getTheEntry().getEditLink().getHref()),
				contact.getTheEntry());
	}

	public void DeleteContactFromGroup(GoogleContact contact, String group)
			throws IOException, ServiceException, RollbackException,
			MyDAOException {

		getAllContactsWithGroup();

		if (!knownGroups.contains(group)) {
			return;
		}

		ArrayList<GoogleContact> contactsInGroup = knownGroups.get(group);
		if (contactsInGroup.contains(contact)) {
			for (int i = 0; i < contact.getTheEntry().getGroupMembershipInfos()
					.size(); i++) {
				if (contact.getTheEntry().getGroupMembershipInfos().get(i)
						.getHref().equals(group)) {
					contact.getTheEntry().getGroupMembershipInfos().remove(i);
					theService.update(new URL(contact.getTheEntry()
							.getEditLink().getHref()), contact.getTheEntry());
					break;
				}
			}
		}
	}

}
